#if _MSC_VER > 1000
#pragma once
#endif

#include <atlbase.h>
#include <limits.h>

#ifndef DWORD_PTR
#define DWORD_PTR DWORD
#endif

#ifndef ASSERT
#define ASSERT _ASSERT
#endif

#ifndef ASSERT_VALID
#define ASSERT_VALID _ASSERT //This is not how MFC does it.
#endif

#ifndef DWORD_PTR
#define DWORD_PTR DWORD
#endif

#ifndef ASSERT
#define ASSERT _ASSERT
#endif

#ifndef ASSERT_VALID
#define ASSERT_VALID _ASSERT //This is not how MFC does it.
#endif

	enum   wtlTraceFlags {
	atlTraceUI = 0x10000000
	};
#if defined(AN_UNDEFINED_SYMBOL
#else
	struct CStringData
	{
		long nRefs;     // reference count
		int nDataLength;
		int nAllocLength;
		// TCHAR data[nAllocLength]

		TCHAR* data()
			{ return (TCHAR*)(this + 1); }
	};
#endif
	// Globals

#if defined(AN_UNDEFINED_SYMBOL)
#else
	// For an empty string, m_pchData will point here
	// (note: avoids special case of checking for NULL m_pchData)
	// empty string data (and locked)
	_declspec(selectany) int rgInitData[] = { -1, 0, 0, 0 };
	_declspec(selectany) CStringData* _atltmpDataNil = (CStringData*)&rgInitData;
	_declspec(selectany) LPCTSTR _atltmpPchNil = (LPCTSTR)(((BYTE*)&rgInitData) + sizeof(CStringData));
#endif
	class CString
	{
	public:
	// Constructorsatlmisc.h requires atlapp.h
		CString();
		CString(const CString& stringSrc);
		CString(TCHAR ch, int nRepeat = 1);
		CString(LPCSTR lpsz);
		CString(LPCWSTR lpsz);
		CString(LPCTSTR lpch, int nLength);
		CString(const unsigned char* psz);

	// Attributes & Operations
		// as an array of characters
		int GetLength() const;
		BOOL IsEmpty() const;
		void Empty();                       // free up the data

		TCHAR GetAt(int nIndex) const;      // 0 based
		TCHAR operator[](int nIndex) const; // same as GetAt
		void SetAt(int nIndex, TCHAR ch);
		operator LPCTSTR() const;           // as a C string

		// overloaded assignment
		const CString& operator=(const CString& stringSrc);
		const CString& operator=(TCHAR ch);
	#ifdef _UNICODE
		const CString& operator=(char ch);
	#endif
		const CString& operator=(LPCSTR lpsz);
		const CString& operator=(LPCWSTR lpsz);
		const CString& operator=(const unsigned char* psz);

		// string concatenation
		const CString& operator+=(const CString& string);
		const CString& operator+=(TCHAR ch);
	#ifdef _UNICODE
		const CString& operator+=(char ch);
	#endif
		const CString& operator+=(LPCTSTR lpsz);

		friend CString __stdcall operator+(const CString& string1, const CString& string2);
		friend CString __stdcall operator+(const CString& string, TCHAR ch);
		friend CString __stdcall operator+(TCHAR ch, const CString& string);
	#ifdef _UNICODE
		friend CString __stdcall operator+(const CString& string, char ch);
		friend CString __stdcall operator+(char ch, const CString& string);
	#endif
		friend CString __stdcall operator+(const CString& string, LPCTSTR lpsz);
		friend CString __stdcall operator+(LPCTSTR lpsz, const CString& string);

		// string comparison
		int Compare(LPCTSTR lpsz) const;         // straight character
		int CompareNoCase(LPCTSTR lpsz) const;   // ignore case
		int Collate(LPCTSTR lpsz) const;         // NLS aware

		// simple sub-string extraction
		CString Mid(int nFirst, int nCount) const;
		CString Mid(int nFirst) const;
		CString Left(int nCount) const;
		CString Right(int nCount) const;

		CString SpanIncluding(LPCTSTR lpszCharSet) const;
		CString SpanExcluding(LPCTSTR lpszCharSet) const;

		// upper/lower/reverse conversion
		void MakeUpper();
		void MakeLower();
		void MakeReverse();

		// trimming whitespace (either side)
		void TrimRight();
		void TrimLeft();

		// advanced manipulation
		// replace occurrences of chOld with chNew
		int Replace(TCHAR chOld, TCHAR chNew);
		// replace occurrences of substring lpszOld with lpszNew;
		// empty lpszNew removes instances of lpszOld
		int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew);
		// remove occurrences of chRemove
		int Remove(TCHAR chRemove);
		// insert character at zero-based index; concatenates
		// if index is past end of string
		int Insert(int nIndex, TCHAR ch);
		// insert substring at zero-based index; concatenates
		// if index is past end of string
		int Insert(int nIndex, LPCTSTR pstr);
		// delete nCount characters starting at zero-based index
		int Delete(int nIndex, int nCount = 1);

		// searching (return starting index, or -1 if not found)
		// look for a single character match
		int Find(TCHAR ch) const;               // like "C" strchr
		int ReverseFind(TCHAR ch) const;
		int FindOneOf(LPCTSTR lpszCharSet) const;

		// look for a specific sub-string
		int Find(LPCTSTR lpszSub) const;        // like "C" strstr

		// Concatentation for non strings
		const CString& Append(int n)
		{
			TCHAR szBuffer[10];
			wsprintf(szBuffer,_T("%d"),n);
			ConcatInPlace(SafeStrlen(szBuffer), szBuffer);
			return *this;
		}

		// simple formatting
		void __cdecl Format(LPCTSTR lpszFormat, ...);
		void __cdecl Format(UINT nFormatID, ...);

		// formatting for localization (uses FormatMessage API)
		BOOL __cdecl FormatMessage(LPCTSTR lpszFormat, ...);
		BOOL __cdecl FormatMessage(UINT nFormatID, ...);

		// Windows support
		BOOL LoadString(UINT nID);          // load from string resource
											// 255 chars max
	#ifndef _UNICODE
		// ANSI <-> OEM support (convert string in place)
		void AnsiToOem();
		void OemToAnsi();
	#endif

	#ifndef _ATL_NO_COM
		// OLE BSTR support (use for OLE automation)
		BSTR AllocSysString() const;
		BSTR SetSysString(BSTR* pbstr) const;
	#endif //!_ATL_NO_COM

		// Access to string implementation buffer as "C" character array
		LPTSTR GetBuffer(int nMinBufLength);
		void ReleaseBuffer(int nNewLength = -1);
		LPTSTR GetBufferSetLength(int nNewLength);
		void FreeExtra();

		// Use LockBuffer/UnlockBuffer to turn refcounting off
		LPTSTR LockBuffer();
		void UnlockBuffer();

	// Implementation
	public:
		~CString();
		int GetAllocLength() const;

		static BOOL __stdcall _IsValidString(LPCWSTR lpsz, int nLength)
		{
			if(lpsz == NULL)
				return FALSE;
			return !::IsBadStringPtrW(lpsz, nLength);
		}

		static BOOL __stdcall _IsValidString(LPCSTR lpsz, int nLength)
		{
			if(lpsz == NULL)
				return FALSE;
			return !::IsBadStringPtrA(lpsz, nLength);
		}

	protected:
		LPTSTR m_pchData;   // pointer to ref counted string data

		// implementation helpers
		CStringData* GetData() const;
		void Init();
		void AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const;
		BOOL AllocBuffer(int nLen);
		void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData);
		BOOL ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data);
		void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData);
		void FormatV(LPCTSTR lpszFormat, va_list argList);
		void CopyBeforeWrite();
		BOOL AllocBeforeWrite(int nLen);
		void Release();
		static void PASCAL Release(CStringData* pData);
		static int PASCAL SafeStrlen(LPCTSTR lpsz);

		static int __stdcall _LoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf)
		{
			_ASSERT(FALSE); /// this function does not work.
			_ASSERT(lpszBuf);
			nMaxBuf = nMaxBuf;
			nID = nID;

			/*
	#ifdef _DEBUG
			// LoadString without annoying warning from the Debug kernel if the
			//  segment containing the string is not present
		//	if (::FindResource(_Module.GetResourceInstance(), MAKEINTRESOURCE((nID>>4) + 1), RT_STRING) == NULL)
			if (::FindResource(g_hResourceInst, MAKEINTRESOURCE((nID>>4) + 1), RT_STRING) == NULL)
			{
				lpszBuf[0] = '\0';
				return 0; // not found
			}
	#endif //_DEBUG
		//	int nLen = ::LoadString(_Module.GetResourceInstance(), nID, lpszBuf, nMaxBuf);
			int nLen = ::LoadString(g_hResourceInst, nID, lpszBuf, nMaxBuf);
			if (nLen == 0)
				lpszBuf[0] = '\0';
			return nLen;
			*/
			return 0;
		}

		static const CString& __stdcall _GetEmptyString()
		{
			return *(CString*)&_atltmpPchNil;
		}

	// CString conversion helpers
		static int __cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
		{
			if (count == 0 && mbstr != NULL)
				return 0;

			int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, (int)count, NULL, NULL);
			ATLASSERT(mbstr == NULL || result <= (int)count);
			if (result > 0)
				mbstr[result - 1] = 0;
			return result;
		}

		static int __cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
		{
			if (count == 0 && wcstr != NULL)
				return 0;

			int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, wcstr, (int)count);
			ATLASSERT(wcstr == NULL || result <= (int)count);
			if (result > 0)
				wcstr[result - 1] = 0;
			return result;
		}

	// Helpers to avoid CRT startup code
		static TCHAR* _cstrchr(const TCHAR* p, TCHAR ch)
		{
			//strchr for '\0' should succeed
			while (*p != 0)
			{
				if (*p == ch)
					break;
				p = ::CharNext(p);
			}
			return (TCHAR*)((*p == ch) ? p : NULL);
		}
		static TCHAR* _cstrchr_db(const TCHAR* p, TCHAR ch1, TCHAR ch2)
		{
			const TCHAR* lpsz = NULL;
			while (*p != 0)
			{
				if (*p == ch1 && *(p + 1) == ch2)
				{
					lpsz = p;
					break;
				}
				p = ::CharNext(p);
			}
			return (TCHAR*)lpsz;
		}
		static TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
		{
			const TCHAR* lpsz = NULL;
			while (*p != 0)
			{
				if (*p == ch)
					lpsz = p;
				p = ::CharNext(p);
			}
			return (TCHAR*)lpsz;
		}
		static TCHAR* _cstrrev(TCHAR* pStr)
		{
			// Optimize NULL, zero-length, and single-char case.
			if ((pStr == NULL) || (pStr[0] == '\0') || (pStr[1] == '\0'))
				return pStr;

			TCHAR* p = pStr;

			while (p[1] != 0)
			{
				TCHAR* pNext = ::CharNext(p);
				if(pNext > p + 1)
				{
					char p1 = *(char*)p;
					*(char*)p = *(char*)(p + 1);
					*(char*)(p + 1) = p1;
				}
				p = pNext;
			}

			TCHAR* q = pStr;

			while (q < p)
			{
				TCHAR t = *q;
				*q = *p;
				*p = t;
				q++;
				p--;
			}
			return (TCHAR*)pStr;
		}
		static TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet)
		{
			int nLen = lstrlen(pCharSet);
			if (nLen == 0)
				return (TCHAR*)pStr;

			const TCHAR* pRet = NULL;
			const TCHAR* pCur = pStr;
			while((pStr = _cstrchr(pCur, *pCharSet)) != NULL)
			{
				if(memcmp(pCur, pCharSet, nLen * sizeof(TCHAR)) == 0)
				{
					pRet = pCur;
					break;
				}
				pCur = ::CharNext(pCur);
			}
			return (TCHAR*) pRet;
		}
		static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet)
		{
			int nRet = 0;
			TCHAR* p = (TCHAR*)pStr;
			while (*p != 0)
			{
				TCHAR* pNext = ::CharNext(p);
				if(pNext > p + 1)
				{
					if(_cstrchr_db(pCharSet, *p, *(p + 1)) == NULL)
						break;
					nRet += 2;
				}
				else
				{
					if(_cstrchr(pCharSet, *p) == NULL)
						break;
					nRet++;
				}
				p = pNext;
			}
			return nRet;
		}
		static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet)
		{
			int nRet = 0;
			TCHAR* p = (TCHAR*)pStr;
			while (*p != 0)
			{
				TCHAR* pNext = ::CharNext(p);
				if(pNext > p + 1)
				{
					if(_cstrchr_db(pCharSet, *p, *(p + 1)) != NULL)
						break;
					nRet += 2;
				}
				else
				{
					if(_cstrchr(pCharSet, *p) != NULL)
						break;
					nRet++;
				}
				p = pNext;
			}
			return nRet;
		}
		static TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet)
		{
			while (*p != 0)
			{
				if (_cstrchr(lpszCharSet, *p) != NULL)
				{
					return (TCHAR*)p;
					break;
				}
				p = ::CharNext(p);
			}
			return NULL;
		}

		static int _cstrisdigit(TCHAR ch)
		{
			WORD type;
			GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
			return (type & C1_DIGIT) == C1_DIGIT;
		}

		static int _cstrisspace(TCHAR ch)
		{
			WORD type;
			GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
			return (type & C1_SPACE) == C1_SPACE;
		}

		static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther)
		{
			return lstrcmp(pstrOne, pstrOther);
		}

		static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther)
		{
			return lstrcmpi(pstrOne, pstrOther);
		}

		static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther)
		{
			int nRet = CompareString(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1);
			ATLASSERT(nRet != 0);
			return nRet - 2;  // Convert to strcmp convention.  This really is documented.
		}

		static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther)
		{
			int nRet = CompareString(GetThreadLocale(), NORM_IGNORECASE, pstrOne, -1, pstrOther, -1);
			ATLASSERT(nRet != 0);
			return nRet - 2;  // Convert to strcmp convention.  This really is documented.
		}

	
	};
	
	#if defined(AN_UNDEFINED_SYMBOL)
	#else
	// Compare helpers
	bool __stdcall operator==(const CString& s1, const CString& s2);
	bool __stdcall operator==(const CString& s1, LPCTSTR s2);
	bool __stdcall operator==(LPCTSTR s1, const CString& s2);
	bool __stdcall operator!=(const CString& s1, const CString& s2);
	bool __stdcall operator!=(const CString& s1, LPCTSTR s2);
	bool __stdcall operator!=(LPCTSTR s1, const CString& s2);
	bool __stdcall operator<(const CString& s1, const CString& s2);
	bool __stdcall operator<(const CString& s1, LPCTSTR s2);
	bool __stdcall operator<(LPCTSTR s1, const CString& s2);
	bool __stdcall operator>(const CString& s1, const CString& s2);
	bool __stdcall operator>(const CString& s1, LPCTSTR s2);
	bool __stdcall operator>(LPCTSTR s1, const CString& s2);
	bool __stdcall operator<=(const CString& s1, const CString& s2);
	bool __stdcall operator<=(const CString& s1, LPCTSTR s2);
	bool __stdcall operator<=(LPCTSTR s1, const CString& s2);
	bool __stdcall operator>=(const CString& s1, const CString& s2);
	bool __stdcall operator>=(const CString& s1, LPCTSTR s2);
	bool __stdcall operator>=(LPCTSTR s1, const CString& s2);
	#endif


	/////////////////////////////////////////////////////////////////////////////
	// CString Implementation
#if defined(AN_UNDEFINED_SYMBOL)
#else
	inline CStringData* CString::GetData() const
		{ ATLASSERT(m_pchData != NULL); return ((CStringData*)m_pchData) - 1; }
	inline void CString::Init()
		{ m_pchData = _GetEmptyString().m_pchData; }
	inline CString::CString(const unsigned char* lpsz)
		{ Init(); *this = (LPCSTR)lpsz; }
	inline const CString& CString::operator=(const unsigned char* lpsz)
		{ *this = (LPCSTR)lpsz; return *this; }
	#ifdef _UNICODE
	inline const CString& CString::operator+=(char ch)
		{ *this += (TCHAR)ch; return *this; }
	inline const CString& CString::operator=(char ch)
		{ *this = (TCHAR)ch; return *this; }
	inline CString __stdcall operator+(const CString& string, char ch)
		{ return string + (TCHAR)ch; }
	inline CString __stdcall operator+(char ch, const CString& string)
		{ return (TCHAR)ch + string; }
	#endif
#endif

	inline int CString::GetLength() const
		{ return GetData()->nDataLength; }
	inline int CString::GetAllocLength() const
		{ return GetData()->nAllocLength; }
	inline BOOL CString::IsEmpty() const
		{ return GetData()->nDataLength == 0; }
	inline CString::operator LPCTSTR() const
		{ return m_pchData; }
	inline int PASCAL CString::SafeStrlen(LPCTSTR lpsz)
		{ return (lpsz == NULL) ? 0 : lstrlen(lpsz); }

	// CString support (windows specific)
	inline int CString::Compare(LPCTSTR lpsz) const
		{ return _cstrcmp(m_pchData, lpsz); }    // MBCS/Unicode aware
	inline int CString::CompareNoCase(LPCTSTR lpsz) const
		{ return _cstrcmpi(m_pchData, lpsz); }   // MBCS/Unicode aware
	// CString::Collate is often slower than Compare but is MBSC/Unicode
	//  aware as well as locale-sensitive with respect to sort order.
	inline int CString::Collate(LPCTSTR lpsz) const
		{ return _cstrcoll(m_pchData, lpsz); }   // locale sensitive

	inline TCHAR CString::GetAt(int nIndex) const
	{
		ATLASSERT(nIndex >= 0);
		ATLASSERT(nIndex < GetData()->nDataLength);
		return m_pchData[nIndex];
	}
	inline TCHAR CString::operator[](int nIndex) const
	{
		// same as GetAt
		ATLASSERT(nIndex >= 0);
		ATLASSERT(nIndex < GetData()->nDataLength);
		return m_pchData[nIndex];
	}
	
#if defined(AN_UNDEFINED_SYMBOL)
#else
	inline bool __stdcall operator==(const CString& s1, const CString& s2)
		{ return s1.Compare(s2) == 0; }
	inline bool __stdcall operator==(const CString& s1, LPCTSTR s2)
		{ return s1.Compare(s2) == 0; }
	inline bool __stdcall operator==(LPCTSTR s1, const CString& s2)
		{ return s2.Compare(s1) == 0; }
	inline bool __stdcall operator!=(const CString& s1, const CString& s2)
		{ return s1.Compare(s2) != 0; }
	inline bool __stdcall operator!=(const CString& s1, LPCTSTR s2)
		{ return s1.Compare(s2) != 0; }
	inline bool __stdcall operator!=(LPCTSTR s1, const CString& s2)
		{ return s2.Compare(s1) != 0; }
	inline bool __stdcall operator<(const CString& s1, const CString& s2)
		{ return s1.Compare(s2) < 0; }
	inline bool __stdcall operator<(const CString& s1, LPCTSTR s2)
		{ return s1.Compare(s2) < 0; }
	inline bool __stdcall operator<(LPCTSTR s1, const CString& s2)
		{ return s2.Compare(s1) > 0; }
	inline bool __stdcall operator>(const CString& s1, const CString& s2)
		{ return s1.Compare(s2) > 0; }
	inline bool __stdcall operator>(const CString& s1, LPCTSTR s2)
		{ return s1.Compare(s2) > 0; }
	inline bool __stdcall operator>(LPCTSTR s1, const CString& s2)
		{ return s2.Compare(s1) < 0; }
	inline bool __stdcall operator<=(const CString& s1, const CString& s2)
		{ return s1.Compare(s2) <= 0; }
	inline bool __stdcall operator<=(const CString& s1, LPCTSTR s2)
		{ return s1.Compare(s2) <= 0; }
	inline bool __stdcall operator<=(LPCTSTR s1, const CString& s2)
		{ return s2.Compare(s1) >= 0; }
	inline bool __stdcall operator>=(const CString& s1, const CString& s2)
		{ return s1.Compare(s2) >= 0; }
	inline bool __stdcall operator>=(const CString& s1, LPCTSTR s2)
		{ return s1.Compare(s2) >= 0; }
	inline bool __stdcall operator>=(LPCTSTR s1, const CString& s2)
		{ return s2.Compare(s1) <= 0; }
#endif

	inline CString::CString()
	{
		Init();
	}

	inline CString::CString(const CString& stringSrc)
	{
		ATLASSERT(stringSrc.GetData()->nRefs != 0);
		if (stringSrc.GetData()->nRefs >= 0)
		{
			ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
			m_pchData = stringSrc.m_pchData;
			InterlockedIncrement(&GetData()->nRefs);
		}
		else
		{
			Init();
			*this = stringSrc.m_pchData;
		}
	}

	inline BOOL CString::AllocBuffer(int nLen)
	// always allocate one extra character for '\0' termination
	// assumes [optimistically] that data length will equal allocation length
	{
		ATLASSERT(nLen >= 0);
		ATLASSERT(nLen <= INT_MAX - 1);    // max size (enough room for 1 extra)

		if (nLen == 0)
		{
			Init();
		}
		else
		{
			CStringData* pData = NULL;
			ATLTRY(pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen + 1) * sizeof(TCHAR)]);
			if(pData == NULL)
				return FALSE;

			pData->nRefs = 1;
			pData->data()[nLen] = '\0';
			pData->nDataLength = nLen;
			pData->nAllocLength = nLen;
			m_pchData = pData->data();
		}

		return TRUE;
	}

	inline void CString::Release()
	{
		if (GetData() != _atltmpDataNil)
		{
			ATLASSERT(GetData()->nRefs != 0);
			if (InterlockedDecrement(&GetData()->nRefs) <= 0)
				delete[] (BYTE*)GetData();
			Init();
		}
	}

	inline void PASCAL CString::Release(CStringData* pData)
	{
		if (pData != _atltmpDataNil)
		{
			ATLASSERT(pData->nRefs != 0);
			if (InterlockedDecrement(&pData->nRefs) <= 0)
				delete[] (BYTE*)pData;
		}
	}

	inline void CString::Empty()
	{
		if (GetData()->nDataLength == 0)
			return;

		if (GetData()->nRefs >= 0)
			Release();
		else
			*this = _T("");

		ATLASSERT(GetData()->nDataLength == 0);
		ATLASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
	}

	inline void CString::CopyBeforeWrite()
	{
		if (GetData()->nRefs > 1)
		{
			CStringData* pData = GetData();
			Release();
			if(AllocBuffer(pData->nDataLength))
				memcpy(m_pchData, pData->data(), (pData->nDataLength + 1) * sizeof(TCHAR));
		}
		ATLASSERT(GetData()->nRefs <= 1);
	}

	inline BOOL CString::AllocBeforeWrite(int nLen)
	{
		BOOL bRet = TRUE;
		if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
		{
			Release();
			bRet = AllocBuffer(nLen);
		}
		ATLASSERT(GetData()->nRefs <= 1);
		return bRet;
	}

	inline CString::~CString()
	//  free any attached data
	{
		if (GetData() != _atltmpDataNil)
		{
			if (InterlockedDecrement(&GetData()->nRefs) <= 0)
				delete[] (BYTE*)GetData();
		}
	}

	inline void CString::AllocCopy(CString& dest, int nCopyLen, int nCopyIndex,
		 int nExtraLen) const
	{
		// will clone the data attached to this string
		// allocating 'nExtraLen' characters
		// Places results in uninitialized string 'dest'
		// Will copy the part or all of original data to start of new string

		int nNewLen = nCopyLen + nExtraLen;
		if (nNewLen == 0)
		{
			dest.Init();
		}
		else
		{
			if(dest.AllocBuffer(nNewLen))
				memcpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen * sizeof(TCHAR));
		}
	}

	inline CString::CString(LPCTSTR lpsz)
	{
		Init();
		if (lpsz != NULL && HIWORD(lpsz) == NULL)
		{
			UINT nID = LOWORD((DWORD_PTR)lpsz);
			if (!LoadString(nID))
				ATLTRACE2(atlTraceUI, 0, _T("Warning: implicit LoadString(%u) in CString failed\n"), nID);
		}
		else
		{
			int nLen = SafeStrlen(lpsz);
			if (nLen != 0)
			{
				if(AllocBuffer(nLen))
					memcpy(m_pchData, lpsz, nLen * sizeof(TCHAR));
			}
		}
	}

	#ifdef _UNICODE
	inline CString::CString(LPCSTR lpsz)
	{
		Init();
		int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
		if (nSrcLen != 0)
		{
			if(AllocBuffer(nSrcLen))
			{
				_mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
				ReleaseBuffer();
			}
		}
	}
	#else //_UNICODE
	inline CString::CString(LPCWSTR lpsz)
	{
		Init();
		int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
		if (nSrcLen != 0)
		{
			if(AllocBuffer(nSrcLen * 2))
			{
				_wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
				ReleaseBuffer();
			}
		}
	}
	#endif //!_UNICODE

	// Assignment operators
	//  All assign a new value to the string
	//      (a) first see if the buffer is big enough
	//      (b) if enough room, copy on top of old buffer, set size and type
	//      (c) otherwise free old string data, and create a new one
	//
	//  All routines return the new string (but as a 'const CString&' so that
	//      assigning it again will cause a copy, eg: s1 = s2 = "hi there".
	//

	inline void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
	{
		if(AllocBeforeWrite(nSrcLen))
		{
			memcpy(m_pchData, lpszSrcData, nSrcLen * sizeof(TCHAR));
			GetData()->nDataLength = nSrcLen;
			m_pchData[nSrcLen] = '\0';
		}
	}

	inline const CString& CString::operator=(const CString& stringSrc)
	{
		if (m_pchData != stringSrc.m_pchData)
		{
			if ((GetData()->nRefs < 0 && GetData() != _atltmpDataNil) || stringSrc.GetData()->nRefs < 0)
			{
				// actual copy necessary since one of the strings is locked
				AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
			}
			else
			{
				// can just copy references around
				Release();
				ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
				m_pchData = stringSrc.m_pchData;
				InterlockedIncrement(&GetData()->nRefs);
			}
		}
		return *this;
	}

	inline const CString& CString::operator=(LPCTSTR lpsz)
	{
		ATLASSERT(lpsz == NULL || _IsValidString(lpsz, FALSE));
		AssignCopy(SafeStrlen(lpsz), lpsz);
		return *this;
	}

	#ifdef _UNICODE
	inline const CString& CString::operator=(LPCSTR lpsz)
	{
		int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
		if(AllocBeforeWrite(nSrcLen))
		{
			_mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
			ReleaseBuffer();
		}
		return *this;
	}
	#else //!_UNICODE
	inline const CString& CString::operator=(LPCWSTR lpsz)
	{
		int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
		if(AllocBeforeWrite(nSrcLen * 2))
		{
			_wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
			ReleaseBuffer();
		}
		return *this;
	}
	#endif  //!_UNICODE

	// Concatenation
	// NOTE: "operator+" is done as friend functions for simplicity
	//      There are three variants:
	//          CString + CString
	// and for ? = TCHAR, LPCTSTR
	//          CString + ?
	//          ? + CString

	inline BOOL CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
		int nSrc2Len, LPCTSTR lpszSrc2Data)
	{
	  // -- master concatenation routine
	  // Concatenate two sources
	  // -- assume that 'this' is a new CString object

		BOOL bRet = TRUE;
		int nNewLen = nSrc1Len + nSrc2Len;
		if (nNewLen != 0)
		{
			bRet = AllocBuffer(nNewLen);
			if (bRet)
			{
				memcpy(m_pchData, lpszSrc1Data, nSrc1Len * sizeof(TCHAR));
				memcpy(m_pchData + nSrc1Len, lpszSrc2Data, nSrc2Len * sizeof(TCHAR));
			}
		}
		return bRet;
	}
#if defined(AN_UNDEFINED_SYMBOL)
#else
	inline CString __stdcall operator+(const CString& string1, const CString& string2)
	{
		CString s;
		s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData);
		return s;
	}

	inline CString __stdcall operator+(const CString& string, LPCTSTR lpsz)
	{
		ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz, FALSE));
		CString s;
		s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz);
		return s;
	}

	inline CString __stdcall operator+(LPCTSTR lpsz, const CString& string)
	{
		ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz, FALSE));
		CString s;
		s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData);
		return s;
	}
#endif

	inline void CString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
	{
		//  -- the main routine for += operators

		// concatenating an empty string is a no-op!
		if (nSrcLen == 0)
			return;

		// if the buffer is too small, or we have a width mis-match, just
		//   allocate a new buffer (slow but sure)
		if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
		{
			// we have to grow the buffer, use the ConcatCopy routine
			CStringData* pOldData = GetData();
			if (ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData))
			{
				ATLASSERT(pOldData != NULL);
				CString::Release(pOldData);
			}
		}
		else
		{
			// fast concatenation when buffer big enough
			memcpy(m_pchData + GetData()->nDataLength, lpszSrcData, nSrcLen * sizeof(TCHAR));
			GetData()->nDataLength += nSrcLen;
			ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
			m_pchData[GetData()->nDataLength] = '\0';
		}
	}

	inline const CString& CString::operator+=(LPCTSTR lpsz)
	{
		ATLASSERT(lpsz == NULL || _IsValidString(lpsz, FALSE));
		ConcatInPlace(SafeStrlen(lpsz), lpsz);
		return *this;
	}

	inline const CString& CString::operator+=(TCHAR ch)
	{
		ConcatInPlace(1, &ch);
		return *this;
	}

	inline const CString& CString::operator+=(const CString& string)
	{
		ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
		return *this;
	}

	inline LPTSTR CString::GetBuffer(int nMinBufLength)
	{
		ATLASSERT(nMinBufLength >= 0);

		if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
		{
			// we have to grow the buffer
			CStringData* pOldData = GetData();
			int nOldLen = GetData()->nDataLength;   // AllocBuffer will tromp it
			if (nMinBufLength < nOldLen)
				nMinBufLength = nOldLen;

			if(AllocBuffer(nMinBufLength))
			{
				memcpy(m_pchData, pOldData->data(), (nOldLen + 1) * sizeof(TCHAR));
				GetData()->nDataLength = nOldLen;
				CString::Release(pOldData);
			}
		}
		ATLASSERT(GetData()->nRefs <= 1);

		// return a pointer to the character storage for this string
		ATLASSERT(m_pchData != NULL);
		return m_pchData;
	}

	inline void CString::ReleaseBuffer(int nNewLength)
	{
		CopyBeforeWrite();  // just in case GetBuffer was not called

		if (nNewLength == -1)
			nNewLength = lstrlen(m_pchData); // zero terminated

		ATLASSERT(nNewLength <= GetData()->nAllocLength);
		GetData()->nDataLength = nNewLength;
		m_pchData[nNewLength] = '\0';
	}

	inline LPTSTR CString::GetBufferSetLength(int nNewLength)
	{
		ATLASSERT(nNewLength >= 0);

		GetBuffer(nNewLength);
		GetData()->nDataLength = nNewLength;
		m_pchData[nNewLength] = '\0';
		return m_pchData;
	}

	inline void CString::FreeExtra()
	{
		ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
		if (GetData()->nDataLength != GetData()->nAllocLength)
		{
			CStringData* pOldData = GetData();
			if(AllocBuffer(GetData()->nDataLength))
			{
				memcpy(m_pchData, pOldData->data(), pOldData->nDataLength * sizeof(TCHAR));
				ATLASSERT(m_pchData[GetData()->nDataLength] == '\0');
				CString::Release(pOldData);
			}
		}
		ATLASSERT(GetData() != NULL);
	}

	inline LPTSTR CString::LockBuffer()
	{
		LPTSTR lpsz = GetBuffer(0);
		GetData()->nRefs = -1;
		return lpsz;
	}

	inline void CString::UnlockBuffer()
	{
		ATLASSERT(GetData()->nRefs == -1);
		if (GetData() != _atltmpDataNil)
			GetData()->nRefs = 1;
	}

	inline int CString::Find(TCHAR ch) const
	{
		// find first single character
		LPTSTR lpsz = _cstrchr(m_pchData, (_TUCHAR)ch);

		// return -1 if not found and index otherwise
		return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
	}

	inline int CString::FindOneOf(LPCTSTR lpszCharSet) const
	{
		ATLASSERT(_IsValidString(lpszCharSet, FALSE));
		LPTSTR lpsz = _cstrpbrk(m_pchData, lpszCharSet);
		return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
	}

	inline void CString::MakeUpper()
	{
		CopyBeforeWrite();
		CharUpper(m_pchData);
	}

	inline void CString::MakeLower()
	{
		CopyBeforeWrite();
		CharLower(m_pchData);
	}

	inline void CString::MakeReverse()
	{
		CopyBeforeWrite();
		_cstrrev(m_pchData);
	}

	inline void CString::SetAt(int nIndex, TCHAR ch)
	{
		ATLASSERT(nIndex >= 0);
		ATLASSERT(nIndex < GetData()->nDataLength);

		CopyBeforeWrite();
		m_pchData[nIndex] = ch;
	}

	#ifndef _UNICODE
	inline void CString::AnsiToOem()
	{
		CopyBeforeWrite();
		::AnsiToOem(m_pchData, m_pchData);
	}
	inline void CString::OemToAnsi()
	{
		CopyBeforeWrite();
		::OemToAnsi(m_pchData, m_pchData);
	}
	#endif

	inline CString::CString(TCHAR ch, int nLength)
	{
		ATLASSERT(!_istlead(ch));    // can't create a lead byte string
		Init();
		if (nLength >= 1)
		{
			if(AllocBuffer(nLength))
			{
	#ifdef _UNICODE
				for (int i = 0; i < nLength; i++)
					m_pchData[i] = ch;
	#else
				memset(m_pchData, ch, nLength);
	#endif
			}
		}
	}

	inline CString::CString(LPCTSTR lpch, int nLength)
	{
		Init();
		if (nLength != 0)
		{
			if(AllocBuffer(nLength))
				memcpy(m_pchData, lpch, nLength * sizeof(TCHAR));
		}
	}

	inline const CString& CString::operator=(TCHAR ch)
	{
		ATLASSERT(!_istlead(ch));    // can't set single lead byte
		AssignCopy(1, &ch);
		return *this;
	}
#if defined(AN_UNDEFINED_SYMBOL)
#else
	inline CString __stdcall operator+(const CString& string1, TCHAR ch)
	{
		CString s;
		s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
		return s;
	}

	inline CString __stdcall operator+(TCHAR ch, const CString& string)
	{
		CString s;
		s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
		return s;
	}
#endif
	inline CString CString::Mid(int nFirst) const
	{
		return Mid(nFirst, GetData()->nDataLength - nFirst);
	}

	inline CString CString::Mid(int nFirst, int nCount) const
	{
		// out-of-bounds requests return sensible things
		if (nFirst < 0)
			nFirst = 0;
		if (nCount < 0)
			nCount = 0;

		if (nFirst + nCount > GetData()->nDataLength)
			nCount = GetData()->nDataLength - nFirst;
		if (nFirst > GetData()->nDataLength)
			nCount = 0;

		CString dest;
		AllocCopy(dest, nCount, nFirst, 0);
		return dest;
	}

	inline CString CString::Right(int nCount) const
	{
		if (nCount < 0)
			nCount = 0;
		else if (nCount > GetData()->nDataLength)
			nCount = GetData()->nDataLength;

		CString dest;
		AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
		return dest;
	}

	inline CString CString::Left(int nCount) const
	{
		if (nCount < 0)
			nCount = 0;
		else if (nCount > GetData()->nDataLength)
			nCount = GetData()->nDataLength;

		CString dest;
		AllocCopy(dest, nCount, 0, 0);
		return dest;
	}

	// strspn equivalent
	inline CString CString::SpanIncluding(LPCTSTR lpszCharSet) const
	{
		ATLASSERT(_IsValidString(lpszCharSet, FALSE));
		return Left(_cstrspn(m_pchData, lpszCharSet));
	}

	// strcspn equivalent
	inline CString CString::SpanExcluding(LPCTSTR lpszCharSet) const
	{
		ATLASSERT(_IsValidString(lpszCharSet, FALSE));
		return Left(_cstrcspn(m_pchData, lpszCharSet));
	}

	inline int CString::ReverseFind(TCHAR ch) const
	{
		// find last single character
		LPTSTR lpsz = _cstrrchr(m_pchData, (_TUCHAR)ch);

		// return -1 if not found, distance from beginning otherwise
		return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
	}

	// find a sub-string (like strstr)
	inline int CString::Find(LPCTSTR lpszSub) const
	{
		ATLASSERT(_IsValidString(lpszSub, FALSE));

		// find first matching substring
		LPTSTR lpsz = _cstrstr(m_pchData, lpszSub);

		// return -1 for not found, distance from beginning otherwise
		return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
	}

	inline void CString::FormatV(LPCTSTR lpszFormat, va_list argList)
	{
		ATLASSERT(_IsValidString(lpszFormat, FALSE));

		enum _FormatModifiers
		{
			FORCE_ANSI =	0x10000,
			FORCE_UNICODE =	0x20000,
			FORCE_INT64 =	0x40000
		};

		va_list argListSave = argList;

		// make a guess at the maximum length of the resulting string
		int nMaxLen = 0;
		for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = ::CharNext(lpsz))
		{
			// handle '%' character, but watch out for '%%'
			if (*lpsz != '%' || *(lpsz = ::CharNext(lpsz)) == '%')
			{
				nMaxLen += (int)lstrlen(lpsz);
				continue;
			}

			int nItemLen = 0;

			// handle '%' character with format
			int nWidth = 0;
			for (; *lpsz != '\0'; lpsz = ::CharNext(lpsz))
			{
				// check for valid flags
				if (*lpsz == '#')
					nMaxLen += 2;   // for '0x'
				else if (*lpsz == '*')
					nWidth = va_arg(argList, int);
				else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' || *lpsz == ' ');
				else // hit non-flag character
					break;
			}
			// get width and skip it
			if (nWidth == 0)
			{
				// width indicated by
				nWidth = _ttoi(lpsz);
				for (; *lpsz != '\0' && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
					;
			}
			ATLASSERT(nWidth >= 0);

			int nPrecision = 0;
			if (*lpsz == '.')
			{
				// skip past '.' separator (width.precision)
				lpsz = ::CharNext(lpsz);

				// get precision and skip it
				if (*lpsz == '*')
				{
					nPrecision = va_arg(argList, int);
					lpsz = ::CharNext(lpsz);
				}
				else
				{
					nPrecision = _ttoi(lpsz);
					for (; *lpsz != '\0' && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
						;
				}
				ATLASSERT(nPrecision >= 0);
			}

			// should be on type modifier or specifier
			int nModifier = 0;
			if(lpsz[0] == _T('I') && lpsz[1] == _T('6') && lpsz[2] == _T('4'))
			{
				lpsz += 3;
				nModifier = FORCE_INT64;
			}
			else
			{
				switch (*lpsz)
				{
				// modifiers that affect size
				case 'h':
					nModifier = FORCE_ANSI;
					lpsz = ::CharNext(lpsz);
					break;
				case 'l':
					nModifier = FORCE_UNICODE;
					lpsz = ::CharNext(lpsz);
					break;

				// modifiers that do not affect size
				case 'F':
				case 'N':
				case 'L':
					lpsz = ::CharNext(lpsz);
					break;
				}
			}

			// now should be on specifier
			switch (*lpsz | nModifier)
			{
			// single characters
			case 'c':
			case 'C':
				nItemLen = 2;
				va_arg(argList, TCHAR);
				break;
			case 'c' | FORCE_ANSI:
			case 'C' | FORCE_ANSI:
				nItemLen = 2;
				va_arg(argList, char);
				break;
			case 'c' | FORCE_UNICODE:
			case 'C' | FORCE_UNICODE:
				nItemLen = 2;
				va_arg(argList, WCHAR);
				break;

			// strings
			case 's':
			{
				LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
				if (pstrNextArg == NULL)
				{
					nItemLen = 6;  // "(null)"
				}
				else
				{
					nItemLen = lstrlen(pstrNextArg);
					nItemLen = max(1, nItemLen);
				}
				break;
			}

			case 'S':
			{
	#ifndef _UNICODE
				LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
				if (pstrNextArg == NULL)
				{
					nItemLen = 6;  // "(null)"
				}
				else
				{
					nItemLen = (int)wcslen(pstrNextArg);
					nItemLen = max(1, nItemLen);
				}
	#else
				LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
				if (pstrNextArg == NULL)
				{
					nItemLen = 6; // "(null)"
				}
				else
				{
					nItemLen = lstrlenA(pstrNextArg);
					nItemLen = max(1, nItemLen);
				}
	#endif
				break;
			}

			case 's' | FORCE_ANSI:
			case 'S' | FORCE_ANSI:
			{
				LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
				if (pstrNextArg == NULL)
				{
					nItemLen = 6; // "(null)"
				}
				else
				{
					nItemLen = lstrlenA(pstrNextArg);
					nItemLen = max(1, nItemLen);
				}
				break;
			}

			case 's' | FORCE_UNICODE:
			case 'S' | FORCE_UNICODE:
			{
				LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
				if (pstrNextArg == NULL)
				{
					nItemLen = 6; // "(null)"
				}
				else
				{
					nItemLen = (int)wcslen(pstrNextArg);
					nItemLen = max(1, nItemLen);
				}
				break;
			}
			}

			// adjust nItemLen for strings
			if (nItemLen != 0)
			{
				nItemLen = max(nItemLen, nWidth);
				if (nPrecision != 0)
					nItemLen = min(nItemLen, nPrecision);
			}
			else
			{
				switch (*lpsz)
				{
				// integers
				case 'd':
				case 'i':
				case 'u':
				case 'x':
				case 'X':
				case 'o':
					if (nModifier & FORCE_INT64)
						va_arg(argList, __int64);
					else
						va_arg(argList, int);
					nItemLen = 32;
					nItemLen = max(nItemLen, nWidth + nPrecision);
					break;

	#ifndef _ATL_USE_CSTRING_FLOAT
				case 'e':
				case 'f':
				case 'g':
				case 'G':
					ATLASSERT(!"Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class.");
	#ifndef _DEBUG
					::OutputDebugString(_T("Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class."));
					::DebugBreak();
	#endif //!_DEBUG
					break;
	#else //_ATL_USE_CSTRING_FLOAT
				case 'e':
				case 'g':
				case 'G':
					va_arg(argList, double);
					nItemLen = 128;
					nItemLen = max(nItemLen, nWidth + nPrecision);
					break;
				case 'f':
					{
						double f;
						LPTSTR pszTemp;

						// 312 == strlen("-1+(309 zeroes).")
						// 309 zeroes == max precision of a double
						// 6 == adjustment in case precision is not specified,
						//   which means that the precision defaults to 6
						pszTemp = (LPTSTR)_alloca(max(nWidth, 312 + nPrecision + 6));

						f = va_arg(argList, double);
						_stprintf(pszTemp, _T( "%*.*f" ), nWidth, nPrecision + 6, f);
						nItemLen = _tcslen(pszTemp);
					}
					break;
	#endif //_ATL_USE_CSTRING_FLOAT

				case 'p':
					va_arg(argList, void*);
					nItemLen = 32;
					nItemLen = max(nItemLen, nWidth + nPrecision);
					break;

				// no output
				case 'n':
					va_arg(argList, int*);
					break;

				default:
					ATLASSERT(FALSE);  // unknown formatting option
				}
			}

			// adjust nMaxLen for output nItemLen
			nMaxLen += nItemLen;
		}

		GetBuffer(nMaxLen);
	#ifndef _ATL_USE_CSTRING_FLOAT
		int nRet = wvsprintf(m_pchData, lpszFormat, argListSave);
	#else //_ATL_USE_CSTRING_FLOAT
		int nRet = _vstprintf(m_pchData, lpszFormat, argListSave);
	#endif //_ATL_USE_CSTRING_FLOAT
		nRet;	// ref
		ATLASSERT(nRet <= GetAllocLength());
		ReleaseBuffer();

		va_end(argListSave);
	}

	// formatting (using wsprintf style formatting)
	inline void __cdecl CString::Format(LPCTSTR lpszFormat, ...)
	{
		ATLASSERT(_IsValidString(lpszFormat, FALSE));

		va_list argList;
		va_start(argList, lpszFormat);
		FormatV(lpszFormat, argList);
		va_end(argList);
	}

	inline void __cdecl CString::Format(UINT nFormatID, ...)
	{
		CString strFormat;
		BOOL bRet = strFormat.LoadString(nFormatID);
		bRet;	// ref
		ATLASSERT(bRet != 0);

		va_list argList;
		va_start(argList, nFormatID);
		FormatV(strFormat, argList);
		va_end(argList);
	}

	// formatting (using FormatMessage style formatting)
	inline BOOL __cdecl CString::FormatMessage(LPCTSTR lpszFormat, ...)
	{
		// format message into temporary buffer lpszTemp
		va_list argList;
		va_start(argList, lpszFormat);
		LPTSTR lpszTemp;
		BOOL bRet = TRUE;

		if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
				lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
			bRet = FALSE;

		// assign lpszTemp into the resulting string and free the temporary
		*this = lpszTemp;
		LocalFree(lpszTemp);
		va_end(argList);
		return bRet;
	}

	inline BOOL __cdecl CString::FormatMessage(UINT nFormatID, ...)
	{
		// get format string from string table
		CString strFormat;
		BOOL bRetTmp = strFormat.LoadString(nFormatID);
		bRetTmp;	// ref
		ATLASSERT(bRetTmp != 0);

		// format message into temporary buffer lpszTemp
		va_list argList;
		va_start(argList, nFormatID);
		LPTSTR lpszTemp;
		BOOL bRet = TRUE;

		if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
				strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
			bRet = FALSE;

		// assign lpszTemp into the resulting string and free lpszTemp
		*this = lpszTemp;
		LocalFree(lpszTemp);
		va_end(argList);
		return bRet;
	}

	inline void CString::TrimRight()
	{
		CopyBeforeWrite();

		// find beginning of trailing spaces by starting at beginning (DBCS aware)
		LPTSTR lpsz = m_pchData;
		LPTSTR lpszLast = NULL;
		while (*lpsz != '\0')
		{
			if (_cstrisspace(*lpsz))
			{
				if (lpszLast == NULL)
					lpszLast = lpsz;
			}
			else
			{
				lpszLast = NULL;
			}
			lpsz = ::CharNext(lpsz);
		}

		if (lpszLast != NULL)
		{
			// truncate at trailing space start
			*lpszLast = '\0';
			GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
		}
	}

	inline void CString::TrimLeft()
	{
		CopyBeforeWrite();

		// find first non-space character
		LPCTSTR lpsz = m_pchData;
		while (_cstrisspace(*lpsz))
			lpsz = ::CharNext(lpsz);

		// fix up data and length
		int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
		memmove(m_pchData, lpsz, (nDataLength + 1) * sizeof(TCHAR));
		GetData()->nDataLength = nDataLength;
	}

	inline int CString::Delete(int nIndex, int nCount /* = 1 */)
	{
		if (nIndex < 0)
			nIndex = 0;
		int nNewLength = GetData()->nDataLength;
		if (nCount > 0 && nIndex < nNewLength)
		{
			CopyBeforeWrite();
			int nBytesToCopy = nNewLength - (nIndex + nCount) + 1;

			memmove(m_pchData + nIndex, m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
			GetData()->nDataLength = nNewLength - nCount;
		}

		return nNewLength;
	}

	inline int CString::Insert(int nIndex, TCHAR ch)
	{
		CopyBeforeWrite();

		if (nIndex < 0)
			nIndex = 0;

		int nNewLength = GetData()->nDataLength;
		if (nIndex > nNewLength)
			nIndex = nNewLength;
		nNewLength++;

		if (GetData()->nAllocLength < nNewLength)
		{
			CStringData* pOldData = GetData();
			LPTSTR pstr = m_pchData;
			if(!AllocBuffer(nNewLength))
				return -1;
			memcpy(m_pchData, pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
			CString::Release(pOldData);
		}

		// move existing bytes down
		memmove(m_pchData + nIndex + 1, m_pchData + nIndex, (nNewLength - nIndex) * sizeof(TCHAR));
		m_pchData[nIndex] = ch;
		GetData()->nDataLength = nNewLength;

		return nNewLength;
	}

	inline int CString::Insert(int nIndex, LPCTSTR pstr)
	{
		if (nIndex < 0)
			nIndex = 0;

		int nInsertLength = SafeStrlen(pstr);
		int nNewLength = GetData()->nDataLength;
		if (nInsertLength > 0)
		{
			CopyBeforeWrite();
			if (nIndex > nNewLength)
				nIndex = nNewLength;
			nNewLength += nInsertLength;

			if (GetData()->nAllocLength < nNewLength)
			{
				CStringData* pOldData = GetData();
				LPTSTR pstr = m_pchData;
				if(!AllocBuffer(nNewLength))
					return -1;
				memcpy(m_pchData, pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
				CString::Release(pOldData);
			}

			// move existing bytes down
			memmove(m_pchData + nIndex + nInsertLength, m_pchData + nIndex, (nNewLength - nIndex - nInsertLength + 1) * sizeof(TCHAR));
			memcpy(m_pchData + nIndex, pstr, nInsertLength * sizeof(TCHAR));
			GetData()->nDataLength = nNewLength;
		}

		return nNewLength;
	}

	inline int CString::Replace(TCHAR chOld, TCHAR chNew)
	{
		int nCount = 0;

		// short-circuit the nop case
		if (chOld != chNew)
		{
			// otherwise modify each character that matches in the string
			CopyBeforeWrite();
			LPTSTR psz = m_pchData;
			LPTSTR pszEnd = psz + GetData()->nDataLength;
			while (psz < pszEnd)
			{
				// replace instances of the specified character only
				if (*psz == chOld)
				{
					*psz = chNew;
					nCount++;
				}
				psz = ::CharNext(psz);
			}
		}
		return nCount;
	}

	inline int CString::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
	{
		// can't have empty or NULL lpszOld

		int nSourceLen = SafeStrlen(lpszOld);
		if (nSourceLen == 0)
			return 0;
		int nReplacementLen = SafeStrlen(lpszNew);

		// loop once to figure out the size of the result string
		int nCount = 0;
		LPTSTR lpszStart = m_pchData;
		LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
		LPTSTR lpszTarget;
		while (lpszStart < lpszEnd)
		{
			while ((lpszTarget = _cstrstr(lpszStart, lpszOld)) != NULL)
			{
				nCount++;
				lpszStart = lpszTarget + nSourceLen;
			}
			lpszStart += lstrlen(lpszStart) + 1;
		}

		// if any changes were made, make them
		if (nCount > 0)
		{
			CopyBeforeWrite();

			// if the buffer is too small, just
			//   allocate a new buffer (slow but sure)
			int nOldLength = GetData()->nDataLength;
			int nNewLength =  nOldLength + (nReplacementLen - nSourceLen) * nCount;
			if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
			{
				CStringData* pOldData = GetData();
				LPTSTR pstr = m_pchData;
				if(!AllocBuffer(nNewLength))
					return -1;
				memcpy(m_pchData, pstr, pOldData->nDataLength * sizeof(TCHAR));
				CString::Release(pOldData);
			}
			// else, we just do it in-place
			lpszStart = m_pchData;
			lpszEnd = m_pchData + GetData()->nDataLength;

			// loop again to actually do the work
			while (lpszStart < lpszEnd)
			{
				while ( (lpszTarget = _cstrstr(lpszStart, lpszOld)) != NULL)
				{
					int nBalance = nOldLength - ((int)(DWORD_PTR)(lpszTarget - m_pchData) + nSourceLen);
					memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen, nBalance * sizeof(TCHAR));
					memcpy(lpszTarget, lpszNew, nReplacementLen * sizeof(TCHAR));
					lpszStart = lpszTarget + nReplacementLen;
					lpszStart[nBalance] = '\0';
					nOldLength += (nReplacementLen - nSourceLen);
				}
				lpszStart += lstrlen(lpszStart) + 1;
			}
			ATLASSERT(m_pchData[nNewLength] == '\0');
			GetData()->nDataLength = nNewLength;
		}

		return nCount;
	}

	inline int CString::Remove(TCHAR chRemove)
	{
		CopyBeforeWrite();

		LPTSTR pstrSource = m_pchData;
		LPTSTR pstrDest = m_pchData;
		LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;

		while (pstrSource < pstrEnd)
		{
			if (*pstrSource != chRemove)
			{
				*pstrDest = *pstrSource;
				pstrDest = ::CharNext(pstrDest);
			}
			pstrSource = ::CharNext(pstrSource);
		}
		*pstrDest = '\0';
		int nCount = (int)(DWORD_PTR)(pstrSource - pstrDest);
		GetData()->nDataLength -= nCount;

		return nCount;
	}

	#ifdef _UNICODE
	#define CHAR_FUDGE 1    // one TCHAR unused is good enough
	#else
	#define CHAR_FUDGE 2    // two BYTES unused for case of DBC last char
	#endif

	inline BOOL CString::LoadString(UINT nID)
	{
		// try fixed buffer first (to avoid wasting space in the heap)
		TCHAR szTemp[256];
		int nCount =  sizeof(szTemp) / sizeof(szTemp[0]);
		int nLen = _LoadString(nID, szTemp, nCount);
		if (nCount - nLen > CHAR_FUDGE)
		{
			*this = szTemp;
			return nLen > 0;
		}

		// try buffer size of 512, then larger size until entire string is retrieved
		int nSize = 256;
		do
		{
			nSize += 256;
			nLen = _LoadString(nID, GetBuffer(nSize - 1), nSize);
		} while (nSize - nLen <= CHAR_FUDGE);
		ReleaseBuffer();

		return nLen > 0;
	}

	#ifndef _ATL_NO_COM
	inline BSTR CString::AllocSysString() const
	{
	#if defined(_UNICODE) || defined(OLE2ANSI)
		BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength);
	#else
		int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
			GetData()->nDataLength, NULL, NULL);
		BSTR bstr = ::SysAllocStringLen(NULL, nLen);
		if(bstr != NULL)
			MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen);
	#endif
		return bstr;
	}

	inline BSTR CString::SetSysString(BSTR* pbstr) const
	{
	#if defined(_UNICODE) || defined(OLE2ANSI)
		::SysReAllocStringLen(pbstr, m_pchData, GetData()->nDataLength);
	#else
		int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
			GetData()->nDataLength, NULL, NULL);
		if(::SysReAllocStringLen(pbstr, NULL, nLen))
			MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, *pbstr, nLen);
	#endif
		ATLASSERT(*pbstr != NULL);
		return *pbstr;
	}
	#endif //!_ATL_NO_COM


/////////////////////////////////////////////////////////////////////////////
